/*
 * BlackLightning.PluginLib.Modules.BLPacket
 * Andy Tidball
 * 
 * Project: Black Lightning Plugin Library
 * Copyright: GNU General Public License
 */

using System;
using System.Text;
using System.Text.RegularExpressions;
using BLLib.Net;

namespace BlackLightning.PluginLib.Modules {
	/// <summary>
	/// Abstract base class for packets which are travelling through the module chain from source to destination.
	/// </summary>
	[Serializable]
	public abstract class BLPacket : Packet, ICloneable {
		// member data
		private Program  _Source;       // the program that the packet came from
		private Program  _Destination;  // the program that the packet will be delivered to
		private BLPacket _Previous;     // a reference to this packet's previous state
		private int      _States;       // the number of previous states this packet has held during processing
		private bool     _Suppressed;   // whether or not the packet has been suppressesd from delivery

		/// <summary>
		/// Creates a new BLPacket.
		/// </summary>
		/// <param name="Source">The program that the packet came from.</param>
		/// <param name="Destination">The program that the packet will be delivered to.</param>
		/// <param name="Data">The data string contained in the packet.</param>
		protected BLPacket(Program Source, Program Destination, string Data)
			: base(Data) {
			_Source      = Source;
			_Destination = Destination;
			_Previous    = null;
			_States      = 1;
			_Suppressed  = false;
		}

		/// <summary>
		/// Copy constructor.
		/// </summary>
		/// <param name="P">The BLPacket to create a copy of.</param>
		/// <param name="DeepCopyOrNextState">True to make a deep copy of the given packet, false to instead use the given packet as the new packet's previous state.</param>
		protected BLPacket(BLPacket P, bool DeepCopyOrNextState)
			: base(P) {
			_Source       = P._Source;
			_Destination  = P._Destination;
			_Suppressed   = P._Suppressed;

			if (DeepCopyOrNextState) {
				_Previous = P._Previous == null ? null : P._Previous.Clone() as BLPacket;
				_States   = P._States;
			} else {
				_Previous = P;
				_States   = P._States + 1;
			}
		}

		/// <summary>
		/// Gets the program that the packet came from.
		/// </summary>
		public Program Source {
			get {
				return _Source;
			}
		}

		/// <summary>
		/// Gets the program that the packet will be delivered to.
		/// </summary>
		public Program Destination {
			get {
				return _Destination;
			}
		}

		/// <summary>
		/// Gets the packet's state before being handled by the current Module.
		/// </summary>
		public BLPacket PreviousState {
			get {
				return _Previous;
			}
		}

		/// <summary>
		/// The number of previous states this packet has held while being processed.
		/// </summary>
		public int PreviousStates {
			get {
				return _States;
			}
		}

		/// <summary>
		/// Whether or not this packet has been suppressed from further processing and delivery.
		/// </summary>
		public bool Suppressed {
			get {
				return _Suppressed;
			}
		}

		/// <summary>
		/// Suppresses the packet from any further processing by modules and from delivery to its destination.  The packet is effectively dropped.
		/// </summary>
		public void Suppress() {
			_Suppressed = true;
		}

		/// <summary>
		/// Creates a deep copy of the packet.
		/// </summary>
		/// <returns>A deep copy of the packet.</returns>
		public abstract object Clone();

		/// <summary>
		/// Creates a new Packet with this one being its PreviousState.
		/// </summary>
		/// <returns>A new Packet that represents the next state of this one.</returns>
		internal abstract BLPacket GetNextState();
	}

	/// <summary>
	/// A filter that can be matched against BLPackets.
	/// </summary>
	[Serializable]
	public class BLPacketFilter {
		// member data
		private string    _RawInputString = null; // the string to match against the packet's raw input
		private Regex     _RawInputRegex  = null; // the regular expression to match against the packet's raw input
		private Program?  _Source         = null; // the source program to match against the packet's source
		private Program?  _Destination    = null; // the destination program to match against the packet's destination
		private DateTime? _StartTime      = null; // the datetime that the packet's datetime must be later than or equal to
		private DateTime? _EndTime        = null; // the datetime that the packet's datetime must be earlier than or equal to

		/// <summary>
		/// Creates a new BLPacketFilter.
		/// </summary>
		public BLPacketFilter() {
		}

		/// <summary>
		/// Creates a new BLPacketFilter.
		/// </summary>
		/// <param name="Input">The input string to match against the packet's raw input.</param>
		public BLPacketFilter(string Input) {
			_RawInputString = Input;
		}

		/// <summary>
		/// Creates a new BLPacketFilter.
		/// </summary>
		/// <param name="Input">The regular expression to match against the packet's raw input.</param>
		public BLPacketFilter(Regex Input) {
			_RawInputRegex  = Input;
		}

		/// <summary>
		/// Creates a new BLPacketFilter.
		/// </summary>
		/// <param name="Source">The source program to match against the packet's source.</param>
		/// <param name="Destination">The destination program to match against the packet's destination.</param>
		public BLPacketFilter(Program Source, Program Destination) {
			_Source         = Source;
			_Destination    = Destination;
		}

		/// <summary>
		/// Creates a new BLPacketFilter.
		/// </summary>
		/// <param name="Source">The source program to match against the packet's source.</param>
		/// <param name="Destination">The destination program to match against the packet's destination.</param>
		/// <param name="Input">The input string to match against the packet's raw input.</param>
		public BLPacketFilter(Program Source, Program Destination, string Input) {
			_RawInputString = Input;
			_Source         = Source;
			_Destination    = Destination;
		}

		/// <summary>
		/// Creates a new BLPacketFilter.
		/// </summary>
		/// <param name="Source">The source program to match against the packet's source.</param>
		/// <param name="Destination">The destination program to match against the packet's destination.</param>
		/// <param name="Input">The regular expression to match against the packet's raw input.</param>
		public BLPacketFilter(Program Source, Program Destination, Regex Input) {
			_RawInputRegex  = Input;
			_Source         = Source;
			_Destination    = Destination;
		}

		/// <summary>
		/// Gets or sets the string which is matched against the packet's raw input string.
		/// </summary>
		public string RawInputString {
			get {
				return _RawInputString;
			}
			set {
				_RawInputString = value;
			}
		}

		/// <summary>
		/// Gets or sets the regular expression which is matched against the packet's raw input string.
		/// </summary>
		public Regex RawInputRegex {
			get {
				return _RawInputRegex;
			}
			set {
				_RawInputRegex = value;
			}
		}

		/// <summary>
		/// Gets or sets the program which is matched against the packet's source program.
		/// </summary>
		public Program? Source {
			get {
				return _Source;
			}
			set {
				_Source = value;
			}
		}

		/// <summary>
		/// Gets or sets the program which is matched against the packet's destination program.
		/// </summary>
		public Program? Destination {
			get {
				return _Destination;
			}
			set {
				_Destination = value;
			}
		}

		/// <summary>
		/// Gets or sets the time that the packet's creation time must be later than or equal to.
		/// </summary>
		public DateTime? StartTime {
			get {
				return _StartTime;
			}
			set {
				_StartTime = value;
			}
		}

		/// <summary>
		/// Gets or sets the time that the packet's creation time must be earlier than or equal to.
		/// </summary>
		public DateTime? EndTime {
			get {
				return _EndTime;
			}
			set {
				_EndTime = value;
			}
		}

		/// <summary>
		/// Sets the Source and Destination properties with one method call.
		/// </summary>
		/// <param name="Source">The source program to match against packet's source programs.</param>
		/// <param name="Destination">The destination program to match against packet's destination programs.</param>
		public void SetPrograms(Program? Source, Program? Destination) {
			_Source      = Source;
			_Destination = Destination;
		}

		/// <summary>
		/// Sets the StartTime and EndTime properties with one method call.
		/// </summary>
		/// <param name="StartTime">The time that matched packet's creation time must be later than or equal to.</param>
		/// <param name="EndTime">The time that matched packet's creation time must be earlier than or equal to.</param>
		public void SetCreationTime(DateTime? StartTime, DateTime? EndTime) {
			_StartTime = StartTime;
			_EndTime   = EndTime;
		}

		/// <summary>
		/// Matches the given packet's static data against the filter.
		/// </summary>
		/// <param name="Packet">The packet to match against the filter.</param>
		/// <returns>True if the packet's static data matches the current setup of the filter, false if it does not.</returns>
		public virtual bool StaticMatch(BLPacket Packet) {
			// match the source program
			if (this.Source != null && Packet.Source != this.Source) {
				return false;
			}

			// match the destination program
			if (this.Destination != null && Packet.Destination != this.Destination) {
				return false;
			}

			// match the start time
			if (this.StartTime != null && Packet.CreationTime < this.StartTime) {
				return false;
			}

			// match the end time
			if (this.EndTime != null && Packet.CreationTime > this.EndTime) {
				return false;
			}

			// everything passed
			return true;
		}

		/// <summary>
		/// Matches the given packet's dynamic data against the filter.
		/// </summary>
		/// <param name="Packet">The packet to match against the filter.</param>
		/// <returns>True if the packet's dynamic data matches the current setup of the filter, false if it does not.</returns>
		public virtual bool DynamicMatch(BLPacket Packet) {
			// match the raw input string
			if (this.RawInputString != null && Packet.RawInput != this.RawInputString) {
				return false;
			}

			// match the raw input regular expression
			// Regexes seem to use \n for newlines rather than Environment.NewLine
			// Since we're using the latter, do a replacement before matching
			if (this.RawInputRegex != null && !this.RawInputRegex.IsMatch(Packet.RawInput.Replace(Environment.NewLine, "\n"))) {
				return false;
			}

			// everything passed
			return true;
		}
	}

	/// <summary>
	/// An enumeration of all programs involved with BlackLightning.
	/// </summary>
	public enum Program {
		/// <summary>
		/// The Black Lightning program.
		/// </summary>
		BlackLightning,

		/// <summary>
		/// The game server program that Black Lightning is connected to.
		/// </summary>
		GameServer,

		/// <summary>
		/// The front end program that Black Lightning is connected to.
		/// </summary>
		FrontEnd
	}
}
